package com.example.neuronview

import glm_.vec3.Vec3
import glm_.quat.Quat
import glm_.mat4x4.Mat4
import glm_.glm

class Trackball(radius: Float = 1.0f) {
    private var mRadius: Float = radius
    private var mWindowWidth: Int = 800
    private var mWindowHeight: Int = 600
    private var mCurrentRotation: Quat = Quat(1.0f, 0.0f, 0.0f, 0.0f)
    private var mLastRotation: Quat = Quat(1.0f, 0.0f, 0.0f, 0.0f)
    private var mStartPoint: Vec3 = Vec3()
    private var mIsDragging: Boolean = false

    // 设置轨迹球半径
    fun setRadius(radius: Float) {
        mRadius = radius
    }

    // 开始拖动
    fun startDrag(x: Int, y: Int) {
        mStartPoint = mapToSphere(x, y)
        mLastRotation = mCurrentRotation
        mIsDragging = true
    }

    // 拖动过程
    fun drag(x: Int, y: Int) {
        if (!mIsDragging) return

        val currentPoint = mapToSphere(x, y)

        // 计算旋转轴 (两向量叉乘)
        val axis = glm.cross(mStartPoint, currentPoint)

        // 计算旋转角度 (两向量点乘)
        var dot = glm.dot(mStartPoint, currentPoint)
        // 限制点乘结果在[-1,1]范围内，防止数值误差导致的问题
        dot = glm.clamp(dot, -1.0f, 1.0f)
        val angle = 2.0f * glm.acos(dot)

        // 创建旋转四元数
        if (glm.length(axis) > 0.001f) { // 避免零向量
            val normalizedAxis = glm.normalize(axis)
            val rotation = glm.angleAxis(angle, normalizedAxis)
            mCurrentRotation = rotation * mLastRotation
            mCurrentRotation = glm.normalize(mCurrentRotation)
        }
    }

    // 获取旋转矩阵
    fun getRotationMatrix(): Mat4 {
        return mCurrentRotation.toMat4()
    }

    // 重置旋转
    fun reset() {
        mCurrentRotation = Quat(1.0f, 0.0f, 0.0f, 0.0f)
        mLastRotation = Quat(1.0f, 0.0f, 0.0f, 0.0f)
        mIsDragging = false
    }

    // 设置当前窗口尺寸
    fun setWindowSize(width: Int, height: Int) {
        mWindowWidth = width
        mWindowHeight = height
    }

    fun setDragging(drag: Boolean) {
        mIsDragging = drag
    }

    // 将屏幕坐标映射到轨迹球上的3D点
    private fun mapToSphere(x: Int, y: Int): Vec3 {
        // 将屏幕坐标转换为[-1,1]范围内的标准化设备坐标
        val nx = 2.0f * x / mWindowWidth - 1.0f
        val ny = 1.0f - 2.0f * y / mWindowHeight

        // 计算到中心的距离
        val r = nx * nx + ny * ny

        var point = Vec3()

        if (r <= 1.0f) { // 在球体内部
            point = Vec3(nx, ny, glm.sqrt(1.0f - r))
        } else { // 在球体外部，使用双曲表面上的点
            val s = 1.0f / glm.sqrt(r)
            point=Vec3(nx * s, ny * s, 0.0f)
        }

        // 应用轨迹球半径
        return point
    }
}